package org.smart4j.framework.common.helper;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.smart4j.framework.common.annotation.Aspect;
import org.smart4j.framework.common.aop.AspectProxy;
import org.smart4j.framework.common.aop.Proxy;
import org.smart4j.framework.common.aop.ProxyManager;
import org.smart4j.framework.common.aop.TransactionProxy;
import org.smart4j.framework.common.util.AnnotationUtil;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 切面助手类
 *
 * @author YooLin1c
 * @version 1.0.0
 * @since 2018-02-03 14:43
 */
@Slf4j
public final class AOPHelper {

    static {
        //将目标类与代理对象的关系填充/替换到Bean容器中
        try {
            //获取切面类与被代理Bean集合的映射关系
            Map<Class<?>, Set<Class<?>>> proxyMap = getProxyMap();
            //获取被代理Bean类与其代理链的映射关系
            Map<Class<?>, List<Proxy>> targetMap = getTargetMap(proxyMap);
            for (Map.Entry<Class<?>, List<Proxy>> entry : targetMap.entrySet()) {
                //获取被代理Bean类的信息
                Class<?> targetClass = entry.getKey();
                //获取代理链
                List<Proxy> proxyChain = entry.getValue();
                //创建代理类对象
                Object proxyInstance = ProxyManager.createProxy(targetClass, proxyChain);
                //在Bean容器中覆盖原被代理Bean类的映射关系为切面代理类对象
                BeanHelper.addBean(targetClass, proxyInstance);
            }
        } catch (Exception e) {
            log.error("aop load failed!", e);
        }
    }

    /**
     * 根据@Aspect注解的value属性的注解获取对应的Bean对象集合
     *
     * @param aspect 切面注解
     * @return Set集合
     */
    private static Set<Class<?>> getTargetClassSet(Aspect aspect) {
        if (Objects.isNull(aspect)) {
            return Sets.newHashSet();
        }
        Class<? extends Annotation> annotation = aspect.value();
        if (Objects.isNull(annotation) || Aspect.class.equals(annotation)) {
            return Sets.newHashSet();
        }
        return ClassHelper.getClassSetByAnnotation(annotation);
    }

    /**
     * 获取代理类（切面类）及其目标类集合之间的映射关系（一对多）
     *
     * @return Map集合
     * @throws Exception 异常
     */
    private static Map<Class<?>, Set<Class<?>>> getProxyMap() throws Exception {
        //获取所有AspectProxy抽象类的子类
        Set<Class<?>> subClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
        //过滤留下被@Aspect注解的类的集合，并建立集合中的切面类与被代理Bean的映射关系
        Map<Class<?>, Set<Class<?>>> proxyMap = subClassSet.stream().filter(AnnotationUtil::isAspectAnnotated).collect(Collectors.toMap(
                proxyClass -> proxyClass, proxyClass -> getTargetClassSet(proxyClass.getAnnotation(Aspect.class)))
        );
        //为@Service注解的Bean对象进行事务代理（TransactionProxy：系统默认切面）
        proxyMap.put(TransactionProxy.class, ClassHelper.getServiceClassSet());
        return proxyMap;
    }

    /**
     * 获取目标类与代理对象列表之间的映射关系
     *
     * @param proxyMap 代理类与目标类的映射关系
     * @return 目标类与代理对象列表之间的映射关系
     * @throws Exception 异常
     */
    private static Map<Class<?>, List<Proxy>> getTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
        Map<Class<?>, List<Proxy>> targetMap = new HashMap<>(16);
        for (Map.Entry<Class<?>, Set<Class<?>>> entry : proxyMap.entrySet()) {
            //获取切面类
            Class<?> proxyClass = entry.getKey();
            //获取切面类代理的Bean类集合
            Set<Class<?>> targetClassSet = entry.getValue();
            //创建切面代理类实例
            Proxy proxyInstance = (Proxy) proxyClass.newInstance();
            //遍历被代理Bean类集合，为每个元素添加代理链
            for (Class<?> targetClass : targetClassSet) {
                //如果重复加入代理链中
                if (targetMap.containsKey(targetClass)) {
                    targetMap.get(targetClass).add(proxyInstance);
                } else {
                    targetMap.put(targetClass, Lists.newArrayList(proxyInstance));
                }
            }
        }
        return targetMap;
    }
}
